package aidansmeaton.airport.overlays;

import java.util.ArrayList;

import net.londatiga.android.QuickAction;

import aidansmeaton.airport.AirportOverlayItem;
import aidansmeaton.airport.database.DatabaseHelper;
import aidansmeaton.airport.quickactions.MapQuickAction;
import aidansmeaton.airport.structures.Airport;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.location.Location;
import android.util.Log;
import android.widget.Toast;

import com.google.android.maps.GeoPoint;
import com.google.android.maps.ItemizedOverlay;
import com.google.android.maps.MapView;
import com.google.android.maps.OverlayItem;

public class AirportItemizedOverlay extends ItemizedOverlay<OverlayItem> {

	protected final static int MINIMUM_REDRAW_MOVE = 50;
	protected final static int ZOOM_LIMIT = 16;
	
	private Context context;
	private MapView mapView;
	private GeoPoint lastDrawnCenter;
	private int lastDrawnZoom;
	private DatabaseHelper db;
	private ArrayList<OverlayItem> airportOverlays;
	
	protected QuickAction qa;
	private int passengerLevel;
	private AirportOverlayItem selected;
		
	public AirportItemizedOverlay(Drawable defaultMarker, Context context, MapView mapView, int passengerLevel) {
		super(boundCenter(defaultMarker));
		//super(boundCenterBottom(defaultMarker));
		
		this.context = context;
		this.mapView = mapView;
		this.passengerLevel = passengerLevel;
		
		airportOverlays = new ArrayList<OverlayItem>();
		
		lastDrawnCenter = mapView.getMapCenter();
		lastDrawnZoom = -1; // Set to -1, force it to draw on create
		
		db = DatabaseHelper.getInstance(context);
		
		populate(); // Add this to fix known bug
	}

	protected void addOverlayItem(OverlayItem item) {
		airportOverlays.add(item);
	}
	
	@Override
	protected OverlayItem createItem(int i) {
		return airportOverlays.get(i);
	}

	@Override
	public int size() {
		return airportOverlays.size();
	}
	
	
	/* Getters and setters */
	
	public Context getContext() {
		return context;
	}
	
	public void setContext(Context context) {
		this.context = context;
	}

	public MapView getMapView() {
		return mapView;
	}

	public void setMapView(MapView mapView) {
		this.mapView = mapView;
	}

	public DatabaseHelper getDatabase() {
		return db;
	}

	public void setDatabase(DatabaseHelper db) {
		this.db = db;
	}

	public ArrayList<OverlayItem> getOverlayItems() {
		return airportOverlays;
	}

	public void setOverlayItems(ArrayList<OverlayItem> overlayItems) {
		this.airportOverlays = overlayItems;
	}
	
	
	/**
	 * Draw the overlay if necessary. Will only redraw if the zoom
	 * has changed, or if the user has moved the map by at least a
	 * certain amount.
	 * @param canvas The Canvas on which to draw.
	 * @param mapView The MapView currently being viewed by the user.
	 * @param shadow
	 */
	@Override
	public void draw(Canvas canvas, MapView mapView, boolean shadow) {
		super.draw(canvas, mapView, shadow);
		
		if (shadow)
			return;
		
		int currentZoom = mapView.getZoomLevel();
		GeoPoint currentCenter = mapView.getMapCenter();
		
		int redraw_threshold = 0;
		
		if (currentZoom >= ZOOM_LIMIT) {
			redraw_threshold = MINIMUM_REDRAW_MOVE;
		} else {
			redraw_threshold = (int) (Math.pow((18-currentZoom),4.5));
			Log.w("HELLO", "Zoom: " + currentZoom + "    Thresh: " + redraw_threshold);
		}
		
		/* Check if the user zoomed in or out, or if they
		 * panned around the map. If so, re-populate the overlay.
		 * This method gets called a LOT though, so to save expensive
		 * database queries, we'll only draw the stops again when
		 * the user has panned more than 50m from the last point
		 * at which stops were drawn.
		 */
		if (currentZoom != lastDrawnZoom || distanceBetween(lastDrawnCenter, currentCenter) > redraw_threshold) {
			lastDrawnZoom = currentZoom;
			lastDrawnCenter = currentCenter;
			drawItems();
		}
	}
	
	/**
	 * Calculate the approximate distance between two
	 * objects of type GeoPoint.
	 * @param p1 First point.
	 * @param p2 Second point.
	 * @return Distance between the points as a float.
	 */
	public static float distanceBetween(GeoPoint p1, GeoPoint p2) {
		float[] result = new float[1];
		
		Location.distanceBetween(
				p1.getLatitudeE6() / 1e6, p1.getLongitudeE6() / 1e6,
				p2.getLatitudeE6() / 1e6, p2.getLongitudeE6() /  1e6, result);
		
		return result[0];
	}
	
	
	public void refresh(){
		
		drawItems();
		
	}
	
	
	private void drawItems(){
		
		airportOverlays.clear();
		setLastFocusedIndex(-1);
		populate();
		
		int zoom = mapView.getZoomLevel();
		// If it's too zoomed out to draw the airports, don't draw them
		if (!airportShouldBeDrawn(zoom)){
			return;
		}
		
		GeoPoint mapCenter = mapView.getMapCenter();
		int latitudeSpan = (mapView.getLatitudeSpan() / 2) + 10;
		int longitudeSpan = (mapView.getLongitudeSpan() / 2) + 10;
		int lat1 = mapCenter.getLatitudeE6() - latitudeSpan;
		int lat2 = mapCenter.getLatitudeE6() + latitudeSpan;
		int lon1 = mapCenter.getLongitudeE6() - longitudeSpan;
		int lon2 = mapCenter.getLongitudeE6() + longitudeSpan;
				
		Cursor c = db.getAirportsInBoundary(passengerLevel, lat1 / 10, lat2 / 10, lon1 / 10, lon2 / 10);
			
		c.moveToFirst();		        
        while (!c.isAfterLast()){
        	Airport airport = new Airport(c);
        	AirportOverlayItem overlayitem = new AirportOverlayItem(airport, "", "");
        	this.addOverlayItem(overlayitem);
            c.moveToNext();
        }
	    c.close();
		
		super.populate();
	}

	private boolean airportShouldBeDrawn(int zoom) {
		
		switch (passengerLevel){
		case 5: return true;
		case 4: return (zoom > 4);
		case 3: return (zoom > 5);
		case 2: return (zoom > 6);
		case 1: return (zoom > 7);
		case 0: return (zoom > 8);
		default: return false;
		
		}
	}
	
	
	@Override
	protected boolean onTap(int index) {
		
		selected = (AirportOverlayItem) getOverlayItems().get(index);
		
		/* Center the selected point. */
		getMapView().getController().setCenter(selected.getPoint());
		
		qa = new MapQuickAction(mapView, selected);
		qa.show();
		
		return true;
	}
	
}
